#include "koopa.h"
#include<cassert>
#include<iostream>
#include"visit.hpp"
#include"utils.hpp"
#include<string>
#include<unordered_map>
#include<vector>

#define TYPE_SIZE 4
#define TOTAL_FUNC_NUM 8

static bool bFrist_block=true;

// 标记当前是扫描还是写入
// 记录写入需要多少栈空间
static int need_stack_space=0;
// 当前函数的返回
static bool current_func_return_void;

// 记录当前全局变量个数
static int current_gb_var_count=0, shadow_current_gb_var_count;
// 全局变量的名字映射
static std::unordered_map<std::string, std::string> gb_var_table, shadow_gb_var_table;
static void alloc_global_site(std::string s, std::string var_name){
  gb_var_table[s]=var_name;
}
static bool gb_var_table_contain(std::string s){
  return gb_var_table.find(s)!=gb_var_table.end();
}
static std::string get_gb_var_name(std::string s){
  if(!gb_var_table_contain(s))assert(false);
  return gb_var_table[s];
}

// 记录栈帧位置存放了哪个变量
static std::unordered_map<std::string, int> sp_table, shadow_sp_table;
// 统计所需栈空间
static int sp_count=0;
static void alloc_stack_site(std::string s){
  if(sp_table.find(s)!=sp_table.end())assert(false);
  sp_table[s]=sp_count*TYPE_SIZE;
  sp_count++;
}

// 存储数组的维数
static std::unordered_map<std::string, std::vector<int>> arr_dims_table, shadow_arr_dims_table;
static std::vector<int> get_arr_dims(std::string arr_sym){
  if(arr_dims_table.find(arr_sym)==arr_dims_table.end())assert(false);
  return arr_dims_table[arr_sym];
}

static void set_arr_dims(std::string arr_sym, std::vector<int> dims){
  if(arr_dims_table.find(arr_sym)!=arr_dims_table.end())assert(false);
  arr_dims_table[arr_sym]=dims;
}

static int get_need_stack_space(){
  // 包括一个用来存放返回地址的位置和四个临时寄存器
  return (sp_count+5)*TYPE_SIZE;
}

static void reset_sp_count(){
  sp_count=0;
}

static int get_sp_rel_site(std::string s){
  if(sp_table.find(s)==sp_table.end()){
    assert(false);
  }
  return sp_table[s];
}

// 暂存全局map变量
inline void temp_save_global(){
  shadow_current_gb_var_count=current_gb_var_count;
  shadow_gb_var_table=gb_var_table;
  shadow_sp_table=sp_table;
  shadow_arr_dims_table=arr_dims_table;
}

// 恢复全局map变量
inline void recover_global(){
  current_gb_var_count=shadow_current_gb_var_count;
  gb_var_table=shadow_gb_var_table;
  sp_table=shadow_sp_table;
  arr_dims_table=shadow_arr_dims_table;
}

// 访问 raw program
void Visit(const koopa_raw_program_t &program, std::string& str) {
  // 执行一些其他的必要操作

  // 访问所有全局变量
  Visit(program.values,str);
  // 访问所有函数
  Visit(program.funcs,str);
}

// 访问 raw slice
void Visit(const koopa_raw_slice_t &slice, std::string& str) {
  for (size_t i = 0; i < slice.len; ++i) {
    auto ptr = slice.buffer[i];

    // 根据 slice 的 kind 决定将 ptr 视作何种元素
    switch (slice.kind) {
      case KOOPA_RSIK_FUNCTION:
      // 访问函数
      {
        // 先扫描，假写入
        temp_save_global();
        std::string fake_str;
        Visit(reinterpret_cast<koopa_raw_function_t>(ptr),fake_str);

        need_stack_space=get_need_stack_space();

        reset_sp_count();
        recover_global();

        // 真写入
        Visit(reinterpret_cast<koopa_raw_function_t>(ptr),str);
        reset_sp_count();

        break;
      }
      case KOOPA_RSIK_BASIC_BLOCK:
        // 访问基本块
        Visit(reinterpret_cast<koopa_raw_basic_block_t>(ptr),str);
        break;
      case KOOPA_RSIK_VALUE:
        // 访问指令
        Visit(reinterpret_cast<koopa_raw_value_t>(ptr),str);
        break;
      default:
        // 我们暂时不会遇到其他内容, 于是不对其做任何处理
        assert(false);
    }
  }
}

// 访问函数
void Visit(const koopa_raw_function_t &func, std::string& str) {
  // 跳过全局函数
  static int gb_func_count=0;
  if(gb_func_count++<2*TOTAL_FUNC_NUM){
    return;
  }

  // 执行一些其他的必要操作
  const char *func_pure_name = func->name+1;
  std::string s_func_pure_name=func_pure_name;

  if(func->ty->data.function.ret->tag==KOOPA_RTT_UNIT){
    current_func_return_void=true;
  }
  else{
    current_func_return_void=false;
  }

  bFrist_block=true;

  std::string pre_str="";
  push_tab(str);
  pre_str+=".text \n";
  push_tab(pre_str);
  pre_str+=".globl " + s_func_pure_name +"\n";
  pre_str+= s_func_pure_name + ": " +"\n";

  std::string post_str="";

  // 访问所有基本块
  Visit(func->bbs,post_str);
  int total_need_stack_space=get_need_stack_space();
  if(total_need_stack_space>2047){
    push_tab(pre_str);
    pre_str+="li t0, " +std::to_string(total_need_stack_space)+"\n";
    push_tab(pre_str);
    pre_str+="sub sp, sp, t0\n";
  }
  else{
    push_tab(pre_str);
    pre_str+="addi sp, sp, -" +std::to_string(total_need_stack_space)+"\n";
  }

  str+=pre_str+post_str+"\n";

}

// 访问基本块
void Visit(const koopa_raw_basic_block_t &bb, std::string& str) {
  // 执行一些其他的必要操作
  // ...
  // 访问所有指令
  if(!bFrist_block){
    str+=(bb->name+1);
    str+=":\n";
  }
  bFrist_block=false;

  Visit(bb->insts,str);
}

// 返回立即数指令
void Visit_int(const koopa_raw_integer_t &value, std::string& str) {
    push_tab(str);
    str += "li a0, " + std::to_string(value.value)+"\n";
}

// 访问对应类型指令的函数定义略
// 视需求自行实现
void Visit_ret(const koopa_raw_value_t &raw_value, std::string& str) {
  koopa_raw_return_t value=raw_value->kind.data.ret;

  if(current_func_return_void){
    // 回退栈帧空间
    push_tab(str);
    if(need_stack_space>2047){
      push_tab(str);
      str+="li t1, " +std::to_string(need_stack_space)+"\n";
      push_tab(str);
      str+="add sp, sp, t1\n";
    }
    else{
      str+="addi sp, sp, " +std::to_string(need_stack_space)+"\n";
    }

    // 空返回
    push_tab(str);
    str += "ret\n";
    return;
  }

  if(value.value->kind.tag==KOOPA_RVT_INTEGER){
    Visit_int(value.value->kind.data.integer,str);
  }
  else{
    std::string ret_value="$"+std::to_string((long)value.value);
    // 认为非立即数的都是寄存器
    if(gb_var_table_contain(ret_value)){
      push_tab(str);
      str += "la t0, "+get_gb_var_name(ret_value)+"\n";
      push_tab(str);
      str += "lw t0, 0(t0)\n";
    }
    else{
      int sp_offse=get_sp_rel_site(ret_value);
      if(sp_offse>2047){
        push_tab(str);
        str += "li t1, "+std::to_string(sp_offse)+"\n";
        push_tab(str);
        str += "add sp, sp, t1\n";
        push_tab(str);
        str += "lw t0, 0(sp)\n";
        push_tab(str);
        str += "sub sp, sp, t1\n";
      }
      else{
        push_tab(str);
        str += "lw t0, "+std::to_string(sp_offse)+"(sp)\n";
      }
    }

    push_tab(str);
    str += "mv a0, t0\n";
  }

  // 回退栈帧空间
  if(need_stack_space>2047){
    push_tab(str);
    str+="li t1, " +std::to_string(need_stack_space)+"\n";
    push_tab(str);
    str+="add sp, sp, t1\n";
  }
  else{
    push_tab(str);
    str+="addi sp, sp, " +std::to_string(need_stack_space)+"\n";
  }

  push_tab(str);
  str += "ret\n";
}

void Visit_store_i(const koopa_raw_value_t &raw_value, std::string& str) {
  int left_value=raw_value->kind.data.store.value->kind.data.integer.value;
  const koopa_raw_value_t & dest_raw_value=raw_value->kind.data.store.dest;
  
  push_tab(str);
  str+="li t0, "+std::to_string(left_value)+"\n";

  std::string maybe_dest_sym="$"+std::to_string((long)raw_value->kind.data.store.dest);
  if(gb_var_table_contain(maybe_dest_sym)){
    push_tab(str);
    str += "la t1, "+get_gb_var_name(maybe_dest_sym)+"\n";
    push_tab(str);
    str+="sw t0, 0(t1)\n";
  }
  else{
    int rel_pos_i=get_sp_rel_site("$"+std::to_string((long)dest_raw_value));
    std::string rel_pos_s=std::to_string(rel_pos_i);
    
    std::string var_name=raw_value->kind.data.store.dest->name;

    // 因为数组指针的栈位置存储的只是数组所在的位置，所以...
    if(var_name.substr(0,5)=="%ptr_"){
      if(rel_pos_i>2047){
        push_tab(str);
        str+="li t1, "+rel_pos_s+"\n";
        push_tab(str);
        str+="add sp, sp, t1\n";
        push_tab(str);
        str+="lw t2, "+rel_pos_s+"(sp)\n";
        push_tab(str);
        str+="sub sp, sp, t1\n";
        push_tab(str);
        str+="sw t0, 0(t2)\n";
      }
      else{
        push_tab(str);
        str+="lw t1, "+rel_pos_s+"(sp)\n";
        push_tab(str);
        str+="sw t0, 0(t1)\n";
      } 
    }
    else{
      // 普通变量的栈位置存储的就是其值
      if(rel_pos_i>2047){
        push_tab(str);
        str+="li t1, "+rel_pos_s+"\n";
        push_tab(str);
        str+="add sp, sp, t1\n";
        push_tab(str);
        str+="sw t0, 0(sp)\n";
        push_tab(str);
        str+="sub sp, sp, t1\n";
      }
      else{
        push_tab(str);
        str+="sw t0, "+rel_pos_s+"(sp)\n";
      }        
    }
  }
}

void Visit_store_s(const koopa_raw_value_t &raw_value,  std::string& str) {
  std::string src_sym="$"+std::to_string((long)raw_value->kind.data.store.value);
  std::string dest_sym="$"+std::to_string((long)raw_value->kind.data.store.dest);
  if(gb_var_table_contain(src_sym)){
    push_tab(str);
    str += "la t1, "+get_gb_var_name(src_sym)+"\n";
    push_tab(str);
    str+="lw t0, 0(t1)\n";

    if(gb_var_table_contain(dest_sym)){
      push_tab(str);
      str+="la t1, "+get_gb_var_name(dest_sym)+"\n";
      push_tab(str);
      str+="sw t0, 0(t1)\n";
    }
    else {
      int rel_pos_i=get_sp_rel_site(dest_sym);
      std::string rel_pos_s=std::to_string(rel_pos_i);
      if(rel_pos_i>2047){
        push_tab(str);
        str+="li t1, "+rel_pos_s+"\n";        
        push_tab(str);
        str+="add sp, sp, t1\n";
        push_tab(str);
        str+="sw t0, 0(sp)\n";    
        push_tab(str);
        str+="sub sp, sp, t1\n";  
      }
      else{
        push_tab(str);
        str+="sw t0, "+rel_pos_s+"(sp)\n";      
      }
    }
  }
  else{
    int sp_offse=get_sp_rel_site(src_sym);
    if(sp_offse>2047){
      push_tab(str);
      str+="li t1, "+std::to_string(sp_offse)+"\n";
      push_tab(str);
      str+="add sp, sp, t1\n";
      push_tab(str);
      str+="lw t0, 0(sp)\n";
      push_tab(str);
      str+="sub sp, sp, t1\n";
    }
    else{
      push_tab(str);
      str+="lw t0, "+std::to_string(sp_offse)+"(sp)\n";
    }

    if(gb_var_table_contain(dest_sym)){
      push_tab(str);
      str+="la t1, "+get_gb_var_name(dest_sym)+"\n";
      push_tab(str);
      str+="sw t0, 0(t1)\n";
    }
    else{
      int rel_pos_i=get_sp_rel_site(dest_sym);
      std::string rel_pos_s=std::to_string(rel_pos_i);
      std::string dest_name=raw_value->kind.data.store.dest->name;
      // 因为数组指针的栈位置存储的只是数组所在的位置，所以...
      if(dest_name.substr(0,5)=="%ptr_"){
        if(rel_pos_i>2047){
          push_tab(str);
          str+="li t1, "+rel_pos_s+"\n";
          push_tab(str);
          str+="add sp, sp, t1\n";
          push_tab(str);
          str+="lw t2, 0(sp)\n";
          push_tab(str);
          str+="sw t0, 0(t2)\n";
          push_tab(str);
          str+="sub sp, sp, t1\n";
        }
        else{
          push_tab(str);
          str+="lw t1, "+rel_pos_s+"(sp)\n";
          push_tab(str);
          str+="sw t0, 0(t1)\n";
        }
      }
      else{
        // 普通变量的栈位置存储的就是其值
        if(rel_pos_i>2047){
          push_tab(str);
          str+="li t1, "+rel_pos_s+"\n";
          push_tab(str);
          str+="add sp, sp, t1\n";
          push_tab(str);
          str+="sw t0, 0(sp)\n";
          push_tab(str);
          str+="sub sp, sp, t1\n";
        }
        else{
          push_tab(str);
          str+="sw t0, "+rel_pos_s+"(sp)\n";
        }
      }

    }
  }
}

void Visit_store_param(int param_index, const koopa_raw_value_t &raw_value, std::string& str) {
  std::string dest_sym="$"+std::to_string((long)raw_value->kind.data.store.dest);
    if(param_index<8){
      push_tab(str);
      str+="sw a"+std::to_string(param_index)+", "+std::to_string(get_sp_rel_site(dest_sym))+"(sp)\n";
    }
    else{
      int sp_offse=(param_index-8)*4+need_stack_space;
      if(sp_offse>2047){
        push_tab(str);
        str+="li t1, "+std::to_string(sp_offse)+"\n";
        push_tab(str);
        str+="add sp, sp, t1\n";
        push_tab(str);
        str+="lw t0, 0(sp)\n";
        push_tab(str);
        str+="sub sp, sp, t1\n";
      }
      else{
        push_tab(str);
        str+="lw t0, "+std::to_string((param_index-8)*4+need_stack_space)+"(sp)\n";
      }
  
      int sw_sp_offse=get_sp_rel_site(dest_sym);
      if(sw_sp_offse>2047){
        push_tab(str);
        str+="li t1, "+ std::to_string(sw_sp_offse)+"\n";
        push_tab(str);
        str+="add sp, sp ,t1\n";
        push_tab(str);
        str+="sw t0, 0(sp)\n";
        push_tab(str);
        str+="sub sp, sp ,t1\n";
      }
      else{
        push_tab(str);
        str+="sw t0, "+ std::to_string(sw_sp_offse)+"(sp)\n";
      }
    }
}

void Visit_store_arr(const koopa_raw_value_t &raw_value, std::string& str) {
    int arr_len=raw_value->kind.data.store.value->kind.data.aggregate.elems.len;
    koopa_raw_value_t * elems=(koopa_raw_value_t *)raw_value->kind.data.store.value->kind.data.aggregate.elems.buffer;
  
    int elems_count=0;
    int offset=get_sp_rel_site(std::string("$")+std::to_string((long)raw_value->kind.data.store.dest));

    std::vector<int> dims;
    dims.push_back(arr_len);
    bool watch_stop=false;

    for(int i=0;i<arr_len;i++){
      // todo: 深度优先搜索
      std::vector<koopa_raw_value_t> list_or_elems;
      list_or_elems.push_back(elems[i]);

      while(list_or_elems.size()>0){
        const koopa_raw_value_t &list_or_elem=list_or_elems[list_or_elems.size()-1];
        list_or_elems.pop_back();
        if(list_or_elem->kind.tag==KOOPA_RVT_INTEGER){
          // 为数组分配更多的栈空间
          if(elems_count>0){
            alloc_stack_site(raw_value->kind.data.store.dest->name+std::string("$_")+std::to_string(elems_count));
          }

          push_tab(str);
          str+="li t0, "+std::to_string(list_or_elem->kind.data.integer.value)+"\n";

          if(offset+elems_count*4>2047){
            push_tab(str);
            str+="li t1, "+std::to_string(offset+elems_count*4)+"\n";
            push_tab(str);
            str+="add sp, sp, t1\n";
            push_tab(str);
            str+="sw t0, 0(sp)\n";
            push_tab(str);
            str+="sub sp, sp, t1\n";
          }
          else{
            push_tab(str);
            str+="sw t0, "+std::to_string(offset+elems_count*4)+"(sp)\n";
          }
          watch_stop=true;
          elems_count++;
        }
        else{
          int next_elems_num=list_or_elem->kind.data.aggregate.elems.len;
          koopa_raw_value_t * next_elems=(koopa_raw_value_t *)list_or_elem->kind.data.aggregate.elems.buffer;
          for(int j=next_elems_num-1;j>=0;j--){
            list_or_elems.push_back(next_elems[j]);
          }

          if(!watch_stop){
            dims.push_back(next_elems_num);
          }

        }
      }
    }

    // 加入维数记录表
    set_arr_dims("$"+std::to_string((long)raw_value->kind.data.store.dest), dims);
}

void Visit_load(const koopa_raw_value_t &raw_value, std::string& str) {
  std::string src_sym="$"+std::to_string((long)raw_value->kind.data.load.src);
  std::string dest_sym="$"+std::to_string((long)raw_value);
  std::string src_name=raw_value->kind.data.load.src->name;

  alloc_stack_site(dest_sym);

  if(gb_var_table_contain(src_sym)){
    push_tab(str);
    str+="la t0, "+get_gb_var_name(src_sym)+"\n";

    // 如果是读数组中的列表元素的话
    if(src_name.substr(0,5)=="%ptr_"){
      
    }
    else{
      push_tab(str);
      str+="lw t0, 0(t0)\n";
    }
    
  }
  else{
    int rel_pos_i=get_sp_rel_site("$"+std::to_string((long)raw_value->kind.data.load.src));
    std::string rel_pos_s=std::to_string(rel_pos_i);
    if(rel_pos_i>2047){
      push_tab(str);
      str+="li t1, "+rel_pos_s+"\n";
      push_tab(str);
      str+="add sp, sp, t1\n";
      push_tab(str);
      str+="lw t0, 0(sp)\n";
      push_tab(str);
      str+="sub sp, sp, t1\n";    
    }
    else{
      push_tab(str);
      str+="lw t0, "+rel_pos_s+"(sp)\n";
    }

    // 如果是读数组中的列表元素的话
    if(src_name.substr(0,5)=="%ptr_"){
      push_tab(str);
      str+="lw t0, 0(t0)\n";
    }
  }
  
  int sp_offse=get_sp_rel_site(dest_sym);
  if(sp_offse>2047){
    push_tab(str);
    str+="li t1, "+std::to_string(sp_offse)+"\n";  
    push_tab(str);
    str+="add sp, sp, t1\n";  
    push_tab(str);
    str+="sw t0, 0(sp)\n";  
    push_tab(str);
    str+="sub sp, sp, t1\n";  
  }
  else{
    push_tab(str);
    str+="sw t0, "+std::to_string(sp_offse)+"(sp)\n";  
  }
}

void Visit_alloc(const koopa_raw_value_t & raw_value, std::string& str) {
  if(raw_value->ty->data.pointer.base->tag==KOOPA_RTT_POINTER){
    std::vector<int> dims;
    dims.push_back(-1);
    const koopa_raw_type_kind * next_elem=raw_value->ty->data.pointer.base->data.pointer.base;
    while(next_elem->tag!=KOOPA_RTT_INT32){
      dims.push_back(next_elem->data.array.len);
      next_elem=next_elem->data.array.base;
    }
    set_arr_dims("$"+std::to_string((long)raw_value), dims);
  }
  alloc_stack_site("$"+std::to_string((long)raw_value));
}

void Visit_bin(const koopa_raw_value_t &raw_value, std::string& str) {
    const koopa_raw_binary_t value=raw_value->kind.data.binary;

    std::string left_ctn,right_ctn;
    if(value.lhs->kind.tag==KOOPA_RVT_INTEGER){
      left_ctn=std::to_string(value.lhs->kind.data.integer.value);
    }
    else {
      left_ctn="t2";

      int sp_lhs_offset_i=get_sp_rel_site("$"+std::to_string((long)value.lhs));
      std::string sp_lhs_offset_s=std::to_string(sp_lhs_offset_i);
      if(sp_lhs_offset_i>2047){
        push_tab(str);
        str+="li t1, "+sp_lhs_offset_s+"\n";    
        push_tab(str);
        str+="add sp, sp, t1\n";    
        push_tab(str);
        str+="lw t0, 0(sp)\n";    
        push_tab(str);
        str+="sub sp, sp, t1\n";     
      }
      else{
        push_tab(str);
        str+="lw t0, "+std::to_string(get_sp_rel_site("$"+std::to_string((long)value.lhs)))+"(sp)\n";        
      }

      push_tab(str);
      str+="mv t2, t0\n";
    }

    if(value.rhs->kind.tag==KOOPA_RVT_INTEGER){
      right_ctn=std::to_string(value.rhs->kind.data.integer.value);
    }
    else {
      right_ctn="t3";

      int sp_rhs_offset_i=get_sp_rel_site("$"+std::to_string((long)value.rhs));
      std::string sp_rhs_offset_s=std::to_string(sp_rhs_offset_i);
      if(sp_rhs_offset_i>2047){
        push_tab(str);
        str+="li t1, "+std::to_string(get_sp_rel_site("$"+std::to_string((long)value.rhs)))+"\n"; 
        push_tab(str);
        str+="add sp, sp, t1\n"; 
        push_tab(str);
        str+="lw t0, 0(sp)\n"; 
        push_tab(str);
        str+="sub sp, sp, t1\n"; 
      }
      else{
        push_tab(str);
        str+="lw t0, "+std::to_string(get_sp_rel_site("$"+std::to_string((long)value.rhs)))+"(sp)\n";        
      }


      push_tab(str);
      str+="mv t3, t0\n";
    }

    push_tab(str);
    if(left_ctn[0]!='t'){
      str += "li";
    }
    else{
      str += "mv";
    }
    str += " t0, " + left_ctn+"\n";

    push_tab(str);
    if(right_ctn[0]!='t'){
      str += "li";
    }
    else{
      str += "mv";
    }
    str += " t1, " + right_ctn+"\n";

    std::string ret_regis="t0";
    switch (value.op)
    {
    case KOOPA_RBO_EQ:
      push_tab(str);
      str += "xor t0, t0, t1\n";
      push_tab(str);
      str += "seqz "+ret_regis+", t0\n";
      break;
    case KOOPA_RBO_NOT_EQ:
      push_tab(str);
      str += "xor t0, t0, t1\n";
      push_tab(str);
      str += "snez "+ret_regis+", t0\n";
      break;
    case KOOPA_RBO_ADD:
      push_tab(str);
      str += "add "+ret_regis+", t0, t1\n";
      break;
    case KOOPA_RBO_SUB:
      push_tab(str);
      str += "sub "+ret_regis+", t0, t1\n";
      break;
    case KOOPA_RBO_MUL:
      push_tab(str);
      str += "mul "+ret_regis+", t0, t1\n";
      break;
    case KOOPA_RBO_DIV:
      push_tab(str);
      str += "div "+ret_regis+", t0, t1\n";
      break;
    case KOOPA_RBO_MOD:
      push_tab(str);
      str += "rem "+ret_regis+", t0, t1\n";
      break;
    case KOOPA_RBO_GT:
      push_tab(str);
      str += "sgt "+ret_regis+", t0, t1\n";
      break;
    case KOOPA_RBO_LT:
      push_tab(str);
      str += "slt "+ret_regis+", t0, t1\n";
      break;
    case KOOPA_RBO_GE:
      push_tab(str);
      str += "slt t0, t0, t1\n";
      str += "seqz "+ret_regis+", t0\n";
      break;
    case KOOPA_RBO_LE:
      push_tab(str);
      str += "sgt t0, t0, t1\n";
      str += "seqz "+ret_regis+", t0\n";
      break;
    case KOOPA_RBO_AND:
      push_tab(str);
      str += "and "+ret_regis+", t0, t1\n";
      break;
    case KOOPA_RBO_OR:
      push_tab(str);
      str += "or "+ret_regis+", t0, t1\n";
      break;
    default:
      assert(false);
    }

    std::string current_new_temp_sym="$"+std::to_string((long)raw_value);
    alloc_stack_site(current_new_temp_sym);

    int sp_sw_offset_i=get_sp_rel_site(current_new_temp_sym);
    std::string sp_sw_offset_s=std::to_string(sp_sw_offset_i);
    if(sp_sw_offset_i>2047){
      push_tab(str);
      str+="li t1, " + std::to_string(get_sp_rel_site(current_new_temp_sym))+"\n";   
      push_tab(str);
      str+="add sp, sp, t1\n";   
      push_tab(str);
      str+="sw "+ret_regis+", 0(sp)\n";  
      push_tab(str);
      str+="sub sp, sp, t1\n";  
    }
    else{
      push_tab(str);
      str+="sw "+ret_regis+", " + std::to_string(get_sp_rel_site(current_new_temp_sym))+"(sp)\n";     
    }
}

void Visit_branch(const koopa_raw_branch_t &value, std::string& str){
  if(value.cond->kind.tag==0){
    // 是数字的话直接判断跳转就行
    int const_condition_result=value.cond->kind.data.integer.value;
    push_tab(str);
    if(const_condition_result!=0){
      str += "j "+std::string(value.true_bb->name+1)+"\n";
    }
    else{
      str += "j "+std::string((value.false_bb->name+1))+"\n";
    }
    return;
  }

  std::string current_new_temp_sym="$"+std::to_string((long)value.cond);
  push_tab(str);
  str+="lw t0, "+std::to_string(get_sp_rel_site(current_new_temp_sym))+"(sp)\n";

  push_tab(str);
  str += std::string("bnez t0, ") + (value.true_bb->name+1)+"\n";
  push_tab(str);
  str += "j "+std::string((value.false_bb->name+1))+"\n";
};

void Visit_jump(const koopa_raw_jump_t &value, std::string& str){
  push_tab(str);
  str += "j "+std::string((value.target->name+1))+"\n";
};

void Visit_call(const koopa_raw_value_t &raw_value, std::string& str){
  std::string called_name=raw_value->kind.data.call.callee->name;
  koopa_raw_value_t* args=(koopa_raw_value_t* )raw_value->kind.data.call.args.buffer;
  int args_len=raw_value->kind.data.call.args.len;

  int p_count=0;

  // 参数个数不超过8个时
  while(p_count<8 && p_count<args_len){
    if(args[p_count]->kind.tag==KOOPA_RVT_INTEGER){
      push_tab(str);
      str+="li a"+std::to_string(p_count)+", "+std::to_string(args[p_count]->kind.data.integer.value)+"\n";     
    }
    else{
      int sp_offset_i=get_sp_rel_site("$"+std::to_string((long)args[p_count]));
      std::string sp_offset_s=std::to_string(sp_offset_i);
      if(sp_offset_i>2047){
        push_tab(str);
        str+="li t1, "+sp_offset_s+"\n";   
        push_tab(str);
        str+="add sp, sp, t1\n";   
        push_tab(str);
        str+="lw a"+std::to_string(p_count)+", 0(sp)\n";   
        push_tab(str);
        str+="sub sp, sp, t1\n";   
      }
      else{
        push_tab(str);
        str+="lw a"+std::to_string(p_count)+", "+sp_offset_s+"(sp)\n";       
      }
    }

    p_count++;
  }

  // 参数个数超过8个时
  while(p_count<args_len){
    if(args[p_count]->kind.tag==KOOPA_RVT_INTEGER){
      push_tab(str);
      str+="li t0, "+std::to_string(args[p_count]->kind.data.integer.value)+"\n";     
    }
    else{
      int sp_args_offset_i=get_sp_rel_site("$"+std::to_string((long)args[p_count]));
      std::string sp_args_offset_s=std::to_string(sp_args_offset_i);
      
      if(sp_args_offset_i>2047){
        push_tab(str);
        str+="li t1, "+sp_args_offset_s+"\n";    
        push_tab(str);
        str+="add sp, sp, t1\n";    
        push_tab(str);
        str+="lw t0, 0(sp)\n";   
        push_tab(str);
        str+="sub sp, sp, t1\n";   
      }
      else{
        push_tab(str);
        str+="lw t0, "+std::to_string(get_sp_rel_site("$"+std::to_string((long)args[p_count])))+"(sp)\n";       
      }
    }

    if((p_count-8)*4>2047){
      push_tab(str);
      str+="li t1, "+std::to_string((p_count-8)*4)+"\n";  
      push_tab(str);
      str+="add sp, sp, t1\n";  
      push_tab(str);
      str+="sw t0, 0(sp)\n";  
      push_tab(str);
      str+="sub sp, sp, t1\n";  
    }
    else{
      push_tab(str);
      str+="sw t0, "+std::to_string((p_count-8)*4)+"(sp)\n";     
    }

    p_count++;
  }

  // 保存返回地址
  int ra_offset=need_stack_space-4;
  if(ra_offset>2047){
    push_tab(str);
    str+="li t1, "+std::to_string(ra_offset)+"\n";
    push_tab(str);
    str+="add sp, sp, t1\n";
    push_tab(str);
    str+="sw ra, 0(sp)\n";
    push_tab(str);
    str+="sub sp, sp, t1\n";
  }
  else{
    push_tab(str);
    str+="sw ra, "+std::to_string(ra_offset)+"(sp)\n";
  }

  // 保存调用者应该保存的寄存器
  // 保存临时寄存器t2、t3、t4、t5
  for(int i=0;i<4;i++){
    if(ra_offset-4*(i+1)>2047){
      push_tab(str);
      str+="li t1, "+std::to_string(ra_offset-4*(i+1))+"\n"; 
      push_tab(str);
      str+="add sp, sp, t1\n"; 
      push_tab(str);
      str+="sw t"+std::to_string(i+2)+", 0(sp)\n"; 
      push_tab(str);
      str+="sub sp, sp, t1\n";
    }
    else{
      push_tab(str);
      str+="sw t"+std::to_string(i+2)+", "+std::to_string(ra_offset-4*(i+1))+"(sp)\n";     
    }
  }

  push_tab(str);
  std::string callee_func_name=called_name.substr(1,called_name.size()-1);
  str+="call "+callee_func_name+"\n";

  // 函数返回后恢复寄存器
  // 恢复临时寄存器t2、t3、t4、t5
  for(int i=3;i>=0;i--){
    if(ra_offset-4*(i+1)>2047){
      push_tab(str);
      str+="li t1, "+std::to_string(ra_offset-4*(i+1))+"\n"; 
      push_tab(str);
      str+="add sp, sp, t1\n"; 
      push_tab(str);
      str+="lw t"+std::to_string(i+2)+", 0(sp)\n"; 
      push_tab(str);
      str+="sub sp, sp, t1\n";
    }
    else{
      push_tab(str);
      str+="lw t"+std::to_string(i+2)+", "+std::to_string(ra_offset-4*(i+1))+"(sp)\n";    
    }
  }

  // 恢复ra
  if(ra_offset>2047){
    push_tab(str);
    str+="li t1, "+std::to_string(ra_offset)+"\n"; 
    push_tab(str);
    str+="add sp, sp, t1\n";  
    push_tab(str);
    str+="lw ra, 0(sp)\n"; 
    push_tab(str);
    str+="sub sp, sp, t1\n";    
  }
  else{
    push_tab(str);
    str+="lw ra, "+std::to_string(ra_offset)+"(sp)\n";  
  }

  // 如果需要，就把函数返回赋值
  std::string current_new_temp_sym="$"+std::to_string((long)raw_value);
  alloc_stack_site(current_new_temp_sym);

  int sp_ret_save_offset_i=get_sp_rel_site(current_new_temp_sym);
  std::string sp_ret_save_offset_s=std::to_string(sp_ret_save_offset_i);
  if(sp_ret_save_offset_i>2047){
    push_tab(str);
    str += "li t1, "+sp_ret_save_offset_s+"\n";  
    push_tab(str);
    str += "add sp, sp, t1\n";  
    push_tab(str);
    str += "sw a0, 0(sp)\n";  
    push_tab(str);
    str += "sub sp, sp, t1\n";   
  }
  else{
    push_tab(str);
    str += "sw a0, "+sp_ret_save_offset_s+"(sp)\n";    
  }
}

// 访问全局变量指令
void Visit_global(const koopa_raw_value_t &raw_value,std::string &str){
  std::string var_name="gb_var_"+std::to_string(current_gb_var_count++);
  std::string var_sym="$"+std::to_string((long)raw_value);

  alloc_global_site(var_sym,var_name);

  push_tab(str);
  str+=".data\n";

  push_tab(str);
  str+=".globl "+var_name+"\n";
  str+=var_name+":\n";

  switch (raw_value->kind.data.global_alloc.init->kind.tag)
  {
  case 0: // 普通变量
  {
    if(raw_value->kind.data.global_alloc.init->kind.data.integer.value==0){
      push_tab(str);
      str+=".zero 4\n";
    }
    else{
      push_tab(str);
      str+=".word "+std::to_string(raw_value->kind.data.global_alloc.init->kind.data.integer.value)+"\n";
    }
    break;
  }
  case 3: // 数组
  {
    int arr_len=raw_value->kind.data.global_alloc.init->kind.data.aggregate.elems.len;
    const void ** arr_buffer=raw_value->kind.data.global_alloc.init->kind.data.aggregate.elems.buffer;
    
    std::vector<int> dims;
    dims.push_back(arr_len);
    bool watch_stop=false;
    for(int i=0;i<arr_len;i++){
      const koopa_raw_value_t &elem_value=*((koopa_raw_value_t *)arr_buffer+i);
      // 深度优先搜索
      std::vector<koopa_raw_value_t> list_or_elems;
      list_or_elems.push_back(elem_value);
      while(list_or_elems.size()>0){
        const koopa_raw_value_t &list_or_elem=list_or_elems[list_or_elems.size()-1];
        list_or_elems.pop_back();
        if(list_or_elem->kind.tag==KOOPA_RVT_INTEGER){
          watch_stop=true;
          push_tab(str);
          str+=".word "+std::to_string(list_or_elem->kind.data.integer.value)+"\n";
        }
        else{
          int next_elems_num=list_or_elem->kind.data.aggregate.elems.len;
          koopa_raw_value_t * next_elems=(koopa_raw_value_t *)list_or_elem->kind.data.aggregate.elems.buffer;
          for(int j=next_elems_num-1;j>=0;j--){
            list_or_elems.push_back(next_elems[j]);
          }
          if(!watch_stop){
            dims.push_back(next_elems_num);
          }
        }
      }
    }
    // 加入维数记录表
    set_arr_dims("$"+std::to_string((long)raw_value), dims);

    break;
  }
  default:
    assert(false);
  }

  str+="\n";
}

inline bool isGlobalArr(const koopa_raw_value_t &raw_value){
  std::string sym="$"+std::to_string((long)raw_value);
  if(gb_var_table.find(sym)!=gb_var_table.end()){
    return true;
  }
  return false;
}

// 访问取指针指令
void Visit_getElemPtr(const koopa_raw_value_t &raw_value,std::string &str){
  koopa_raw_value_t src_elem=raw_value->kind.data.get_elem_ptr.src;
  std::vector<int> dims=get_arr_dims("$"+std::to_string((long)src_elem));

  int every_elem_size=1;
  for(int i=1;i<dims.size();i++){
    every_elem_size*=dims[i];
  }

  if(isGlobalArr(raw_value->kind.data.get_elem_ptr.src)){
    push_tab(str);
    std::string gb_var_name=get_gb_var_name("$"+std::to_string((long)raw_value->kind.data.get_elem_ptr.src));
    str+="la t0, "+gb_var_name+"\n";

    if(raw_value->kind.data.get_elem_ptr.index->kind.tag==KOOPA_RVT_INTEGER){
      int index=raw_value->kind.data.get_elem_ptr.index->kind.data.integer.value;
      push_tab(str);
      str+="li t1, "+std::to_string(4*index*every_elem_size)+"\n";
      push_tab(str);
      str+="add t0, t0, t1\n";
    }
    else{
      // 临时符号
      int elem_offset=get_sp_rel_site("$"+std::to_string((long)raw_value->kind.data.get_elem_ptr.index));
      push_tab(str);
      str+="lw t1, "+std::to_string(elem_offset)+"(sp)\n";

      push_tab(str);
      str+="li t2, "+std::to_string(4*every_elem_size)+"\n";
      push_tab(str);
      str+="mul t1, t1, t2\n";

      push_tab(str);
      str+="add t0, t0, t1\n";
    }
  }
  else{
    std::string src_name;
    if(raw_value->kind.data.get_elem_ptr.src->kind.tag==KOOPA_RVT_GET_PTR){
      src_name="%";
    }
    else{
      src_name=raw_value->kind.data.get_elem_ptr.src->name;
    }

    int src_ptr_offset=get_sp_rel_site("$"+std::to_string((long)raw_value->kind.data.get_elem_ptr.src));
    // 如果是变量（@开头），他在栈中的值是它的第一个元素；
    // 如果是数组指针（%开头），他在栈中的值是数组所在内存位置
    if(src_name[0]=='%'){
      // 先从栈中读到偏移值，再把这值加到t0。完成该块后，t0存储了src的数组指针位置
      if(src_ptr_offset>2047){
        push_tab(str);
        str+="li t1, "+std::to_string(src_ptr_offset)+"\n";
        push_tab(str);
        str+="add sp, sp, t1\n";
        push_tab(str);
        str+="lw t0, 0(sp)\n";
        push_tab(str);
        str+="sub sp, sp, t1\n";
      }
      else{
        push_tab(str);
        str+="lw t0, "+std::to_string(src_ptr_offset)+"(sp)\n";
      }
    }
    else if(src_name[0]=='@'){
      push_tab(str);
      str+="mv t0, sp\n";
      push_tab(str);
      str+="li t1, "+std::to_string(src_ptr_offset)+"\n";
      push_tab(str);
      str+="add t0, t0, t1\n";
    }
    else assert(false);

    // 数组偏移
    if(raw_value->kind.data.get_elem_ptr.index->kind.tag==KOOPA_RVT_INTEGER){
      int index_offset=4*raw_value->kind.data.get_elem_ptr.index->kind.data.integer.value;
      push_tab(str);
      str+="li t1, "+std::to_string(index_offset*every_elem_size)+"\n";
      push_tab(str);
      str+="add t0, t0, t1\n";
    }
    else {
      // 临时符号
      int elem_offset=get_sp_rel_site("$"+std::to_string((long)raw_value->kind.data.get_elem_ptr.index));
      push_tab(str);
      str+="lw t1, "+std::to_string(elem_offset)+"(sp)\n";

      push_tab(str);
      str+="li t2, "+std::to_string(4*every_elem_size)+"\n";
      push_tab(str);
      str+="mul t1, t1, t2\n";

      push_tab(str);
      str+="add t0, t0, t1\n";
    }
  }

  // 如果还有其他维数，则表示当前的临时符号指向着一个数组, 此时要记录剩余维数
  if(dims.size()>1){
    std::vector<int> rest_dims;
    for(int i=1;i<dims.size();i++){
      rest_dims.push_back(dims[i]);
    }

    set_arr_dims("$"+std::to_string((long)raw_value),rest_dims);
  }

  std::string dest_sym="$"+std::to_string((long)raw_value);
  alloc_stack_site(dest_sym);
  int sp_offs=get_sp_rel_site(dest_sym);

  if(sp_offs>2047){
    push_tab(str);
    str+="li t1, "+std::to_string(sp_offs)+"\n";
    push_tab(str);
    str+="add sp, sp, t1\n";
    push_tab(str);
    str+="sw t0, 0(sp)\n";
    push_tab(str);
    str+="sub sp, sp, t1\n";
  }
  else{
    push_tab(str);
    str+="sw t0, "+std::to_string(sp_offs)+"(sp)\n"; 
  }
}

// 访问取指针指令
void Visit_getPtr(const koopa_raw_value_t &raw_value,std::string &str){
  std::string src_name=raw_value->kind.data.get_ptr.src->name;
  koopa_raw_value_t arr_raw_value=raw_value->kind.data.get_ptr.src;
  if(arr_raw_value->kind.tag==KOOPA_RVT_LOAD){
    arr_raw_value=raw_value->kind.data.get_ptr.src->kind.data.load.src;
  }
  std::vector<int> dims=get_arr_dims("$"+std::to_string((long)arr_raw_value));

  int every_elem_size=1;
  for(int i=1;i<dims.size();i++){
    every_elem_size*=dims[i];
  }

  if(isGlobalArr(raw_value->kind.data.get_ptr.src)){
    push_tab(str);
    std::string gb_var_name=get_gb_var_name("$"+std::to_string((long)raw_value->kind.data.get_ptr.src));
    str+="la t0, "+gb_var_name+"\n";

    if(raw_value->kind.data.get_ptr.index->kind.tag==KOOPA_RVT_INTEGER){
      int index=raw_value->kind.data.get_ptr.index->kind.data.integer.value;
      push_tab(str);
      str+="li t1, "+std::to_string(4*index*every_elem_size)+"\n";
      push_tab(str);
      str+="add t0, t0, t1\n";
    }
    else{
      // 临时符号
      int elem_offset=get_sp_rel_site("$"+std::to_string((long)raw_value->kind.data.get_ptr.index));
      push_tab(str);
      str+="lw t1, "+std::to_string(elem_offset)+"(sp)\n";

      push_tab(str);
      str+="li t2, "+std::to_string(4*every_elem_size)+"\n";
      push_tab(str);
      str+="mul t1, t1, t2\n";

      push_tab(str);
      str+="add t0, t0, t1\n";
    }
    
  }
  else{
    int src_ptr_offset=get_sp_rel_site("$"+std::to_string((long)raw_value->kind.data.get_ptr.src));
    // 如果是变量（@开头），他在栈中的值是它的第一个元素；
    // 如果是数组指针（%开头），他在栈中的值是数组所在内存位置

    if(src_name[0]=='%'){
      // 先从栈中读到偏移值，再把这值加到t0。完成该块后，t0存储了src的数组指针位置
      if(src_ptr_offset>2047){
        push_tab(str);
        str+="li t1, "+std::to_string(src_ptr_offset)+"\n";
        push_tab(str);
        str+="add sp, sp, t1\n";
        push_tab(str);
        str+="lw t0, 0(sp)\n";
        push_tab(str);
        str+="sub sp, sp, t1\n";
      }
      else{
        push_tab(str);
        str+="lw t0, "+std::to_string(src_ptr_offset)+"(sp)\n";
      }
    }
    else if(src_name[0]=='@'){
      push_tab(str);
      str+="mv t0, sp\n";
      push_tab(str);
      str+="li t1, "+std::to_string(src_ptr_offset)+"\n";
      push_tab(str);
      str+="add t0, t0, t1\n";
    }
    else assert(false);

    // 数组偏移
    if(raw_value->kind.data.get_ptr.index->kind.tag==KOOPA_RVT_INTEGER){
      int index_offset=4*raw_value->kind.data.get_ptr.index->kind.data.integer.value;
      push_tab(str);
      str+="li t1, "+std::to_string(index_offset*every_elem_size)+"\n";
      push_tab(str);
      str+="add t0, t0, t1\n";
    }
    else {
      // 临时符号
      int elem_offset=get_sp_rel_site("$"+std::to_string((long)raw_value->kind.data.get_ptr.index));
      push_tab(str);
      str+="lw t1, "+std::to_string(elem_offset)+"(sp)\n";

      push_tab(str);
      str+="li t2, "+std::to_string(4*every_elem_size)+"\n";
      push_tab(str);
      str+="mul t1, t1, t2\n";

      push_tab(str);
      str+="add t0, t0, t1\n";
    }
  }

  // 把剩余维数放入全局数组维数信息表中，方便后续指令查找
  std::vector<int> rest_dims;
  for(int i=1;i<dims.size();i++){
    rest_dims.push_back(dims[i]);
  }
  set_arr_dims("$"+std::to_string((long)raw_value),rest_dims);

  std::string dest_sym="$"+std::to_string((long)raw_value);
  alloc_stack_site(dest_sym);
  int sp_offs=get_sp_rel_site(dest_sym);

  if(sp_offs>2047){
    push_tab(str);
    str+="li t1, "+std::to_string(sp_offs)+"\n";
    push_tab(str);
    str+="add sp, sp, t1\n";
    push_tab(str);
    str+="sw t0, 0(sp)\n";
    push_tab(str);
    str+="sub sp, sp, t1\n";
  }
  else{
    push_tab(str);
    str+="sw t0, "+std::to_string(sp_offs)+"(sp)\n"; 
  }
}

// 访问指令
void Visit(const koopa_raw_value_t &value, std::string& str) {
  // 根据指令类型判断后续需要如何访问
  const auto &kind = value->kind;
  switch (kind.tag) {
    case KOOPA_RVT_RETURN:
      // 访问 return 指令
      Visit_ret(value, str);
      break;
    case KOOPA_RVT_INTEGER:
      // 访问 integer 指令
      Visit_int(kind.data.integer, str);
      break;
    case KOOPA_RVT_BINARY:
      // 访问 二元运算 指令
      Visit_bin(value, str);
      break;
    case KOOPA_RVT_STORE:
      // 访问 内存存储 指令
      if(kind.data.store.value->kind.tag==12)
        Visit_store_s(value, str);
      else if(kind.data.store.value->kind.tag==0)
        Visit_store_i(value, str);
      else if(kind.data.store.value->kind.tag==4){
        Visit_store_param(kind.data.store.value->kind.data.block_arg_ref.index,value,str);
      }
      else if(kind.data.store.value->kind.tag==15){
        // 这种情况属于调用函数并赋值，如 int x=f(0);
        Visit_store_s(value, str);
      }
      else if(kind.data.store.value->kind.tag==8){
        // 先load，后store
        Visit_store_s(value, str);
      }
      else if(kind.data.store.value->kind.tag==3){
        Visit_store_arr(value,str);
      }
      else {
        std::cout<<"Unimplement tag = "<<kind.data.store.value->kind.tag<<std::endl;
        assert(false);
      }
      break;
    case KOOPA_RVT_ALLOC:
      // 访问 分配栈帧空间 指令
      Visit_alloc(value, str);
      break;
    case KOOPA_RVT_LOAD:
      // 访问 内存加载 指令
      Visit_load(value, str);
      break;
    case KOOPA_RVT_BRANCH:
      // 访问 分支定义 指令
      Visit_branch(kind.data.branch,str);
      break;
    case KOOPA_RVT_JUMP:
      // 访问 跳转 指令
      Visit_jump(kind.data.jump,str);
      break;
    case KOOPA_RVT_CALL:
    // 访问 函数调用 指令
      Visit_call(value ,str);
      break;
    case KOOPA_RVT_GLOBAL_ALLOC:
    // 访问 全局变量 指令
      Visit_global(value, str);
      break;
    case KOOPA_RVT_GET_ELEM_PTR:
    // 访问 取元素 指令
      Visit_getElemPtr(value, str);
      break;
    case KOOPA_RVT_GET_PTR:
    // 访问 取指针 指令
      Visit_getPtr(value, str);
      break;
    default:{
      std::cout<<"Unimplement tag = "<<kind.tag<<std::endl;
      // 其他类型暂时遇不到
      assert(false);
    }
  }
}
